package com.wanna.common.net;

import com.wanna.common.util.Constant;
import com.wanna.common.util.QuietlyUtil;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;


/**
 * @author wanna
 * @since 2018-5-24
 */
public class HttpClient {

    private static int DEFAULT_TIME_OUT = 6000;

    private String url;

    private Map<String, Object> param = new HashMap<>();

    private HttpURLConnection connection = null;

    public HttpClient(String url) {
        this.url = url;
    }

    /**
     * 添加请求参数
     *
     * @param key key
     * @param val val
     * @return
     */
    public HttpClient addParameter(String key, Object val) {
        if (param.containsKey(key)) {
            Object o = param.get(key);
            if (o instanceof Collection) {
                ((Collection) o).add(val);
            } else if (o instanceof Object[]) {
                Object[] oldObjects = (Object[]) o;
                Object[] newObjects = new Object[(oldObjects.length + 1)];
                System.arraycopy(oldObjects, 0, newObjects, 0, oldObjects.length);
                newObjects[newObjects.length - 1] = val;
                param.put(key, newObjects);
            } else {
                Object[] objects = new Object[2];
                objects[0] = o;
                objects[1] = val;
                param.put(key, objects);
            }

        } else {
            param.put(key, val);
        }
        return this;
    }

    /**
     * 添加 map(来源于 Seriel)
     *
     * @param val val
     * @return this
     */
    public HttpClient addParameter(Map<String, Object> val) {
        param.putAll(val);
        return this;
    }

    /**
     * 设置请求参数
     *
     * @param key key
     * @param val val
     * @return
     */
    public HttpClient setParameter(String key, Object val) {
        param.put(key, val);
        return this;
    }

    /**
     * 设置超时时间
     *
     * @param timeout
     * @return
     */
    public HttpClient setConnectTimeout(int timeout) {
        connection.setConnectTimeout(timeout);
        return this;
    }

    /**
     * get 请求
     *
     * @return result
     * @throws IOException exception
     */
    public String get() throws IOException {
        addRequestProperty(Constant.CHARSET, Constant.UTF8);
        return doRequest(Request.GET);
    }

    /**
     * post 请求
     *
     * @return server response
     * @throws IOException exception
     */
    public String post() throws IOException {
        addRequestProperty(Constant.CHARSET, Constant.UTF8);
        return doRequest(Request.POST);
    }

    /**
     * 打开连接
     *
     * @return
     * @throws IOException
     */
    public HttpClient openConnection() throws IOException {
        openConnection(url);
        return this;
    }

    /**
     * 打开连接
     *
     * @return this
     * @throws IOException exception
     */
    public HttpClient openConnection(String url) throws IOException {
        URL uri = new URL(url);
        connection = (HttpURLConnection) uri.openConnection();
        setConnectTimeout(DEFAULT_TIME_OUT);
        addRequestProperty(Constant.CHARSET, Constant.UTF8);
        return this;
    }

    /**
     * 添加请求属性
     *
     * @param key   key
     * @param value value
     * @return this
     */
    public HttpClient addRequestProperty(String key, String value) {
        connection.addRequestProperty(key, value);
        return this;
    }

    /**
     * connect
     *
     * @return this
     * @throws IOException exception
     */
    public HttpClient connect() throws IOException {
        connection.connect();
        return this;
    }

    /**
     * 获取输入流
     *
     * @return inputStream
     * @throws IOException exception
     */
    public InputStream getInputStream() throws IOException {
        return connection.getInputStream();
    }

    /**
     * 获取连接
     *
     * @return connection
     */
    public HttpURLConnection getConnection() {
        return connection;
    }

    /**
     * 获取输出流
     *
     * @return outputStream
     * @throws IOException exception
     */
    public OutputStream getOutputStream() throws IOException {
        return connection.getOutputStream();
    }

    /**
     * disconnect
     */
    public void disconnect() {
        connection.disconnect();
    }

    /**
     * 直接发送请求,不需要手动 openConnection
     *
     * @return server response
     * @throws IOException exception
     */
    public String requestGet() throws IOException {
        return request(Request.GET);
    }

    /**
     * 直接发送请求,不需要手动 openConnection
     *
     * @return result
     * @throws IOException exception
     */
    public String requestPost() throws IOException {
        return request(Request.POST);
    }

    /**
     * 具体请求逻辑
     *
     * @param request 方法类型 Get or Post
     * @return response
     * @throws IOException exception
     */
    public String request(Request request) throws IOException {
        // 请求之前先 openConnection
        openConnection(buildRequestURL(request));

        connection.setRequestMethod(request.name());

        return doRequest(request);
    }

    /**
     * 具体请求逻辑
     *
     * @param request
     * @return response
     * @throws IOException exception
     */
    private String doRequest(Request request) throws IOException {

        try {
            connection.setDoOutput(true);
            connect();

            // requestPost 请求的参数
            if (Request.POST == request) {
                sendPostParameter();
            }

            return getResponse();
        } finally {
            disconnect();
        }
    }

    /**
     * 发送 requestPost 请求的参数
     *
     * @throws IOException exception
     */
    private void sendPostParameter() throws IOException {
        OutputStream out = getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(out, Constant.UTF8);
        PrintWriter printWriter = new PrintWriter(osw);
        printWriter.write(getPostParameterChar());
        printWriter.flush();
        QuietlyUtil.close(printWriter);
    }

    /**
     * 获取响应结果
     *
     * @return response
     * @throws IOException exception
     */
    private String getResponse() throws IOException {
        InputStreamReader reader = null;
        BufferedReader buffer = null;

        try {
            reader = new InputStreamReader(getInputStream(), Constant.UTF8);
            buffer = new BufferedReader(reader);
            StringBuilder builder = new StringBuilder();

            String line;
            while ((line = buffer.readLine()) != null) {
                builder.append(line).append(Constant.NEW_LINE);
            }

            return builder.toString();
        } finally {
            QuietlyUtil.close(buffer, reader);
        }
    }

    /**
     * 获取 requestPost 请求参数 char 数组
     *
     * @return char param
     */
    private char[] getPostParameterChar() {
        if (param.size() == 0) {
            return new char[]{};
        }

        return buildRequestParameter().toCharArray();
    }

    /**
     * 构建请求参数 key = val
     *
     * @return url param
     */
    private String buildRequestParameter() {
        StringBuilder builder = new StringBuilder();

        for (Map.Entry<String, Object> entry : param.entrySet()) {
            builder.append(entry.getKey()).append("=");

            Object value = entry.getValue();
            if (value instanceof Collection) {
                ((Collection) value).forEach(s -> builder.append(encoderParam(s.toString()) + ","));
                builder.deleteCharAt(builder.lastIndexOf(",")).append("&");
            } else if (value instanceof Object[]) {
                Stream.of((Object[]) value).forEach(s -> builder.append(encoderParam(s.toString()) + ","));
                builder.deleteCharAt(builder.lastIndexOf(",")).append("&");
            } else {
                builder.append(encoderParam(value.toString())).append("&");
            }
        }

        return builder.substring(0, builder.length() - 1);
    }

    /**
     * 根据请求类型构建 url
     *
     * @param request GET or POST
     * @return requestURL
     */
    private String buildRequestURL(Request request) {
        switch (request) {
            case GET:
                return buildGetMethodURL();
            case POST:
                return url;
            default:
                return url;
        }
    }

    /**
     * 构建 GET 方法的请求 URL
     *
     * @return requestURL
     */
    private String buildGetMethodURL() {
        if (param.size() == 0) {
            return url;
        }

        String builderParam = buildRequestParameter();
        return url + "?" + builderParam;
    }

    /**
     * 参数 URL 编码
     *
     * @param param 请求参数
     * @return after encoder param
     */
    private String encoderParam(String param) {
        try {
            return URLEncoder.encode(param, Constant.UTF8);
        } catch (UnsupportedEncodingException e) {
            return param;
        }
    }

}
